// valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --num-callers=20 --track-fds=yes  ./ucpile
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
struct element
            { int id;
              char *data;
              struct element *left,*right;
            };
//typedef struct element ELEMENT;

// Special
void suppression_tous(struct element *start, char *data);
int print_top2root(struct element *start);
int print_root2top(struct element *start);
int print_id(struct element *start, int id);

// INVITE COMMANDE
void INVITE(void);
char ligne[64];

// PILE
#define ALLOC_LOG
#if defined ALLOC_LOG
	char buf[32];
	char taille_log[1024];
	int taille_totale;
#endif
struct element *ROOT, *TOP;
void TRIM(char *ligne);
void EMPILE(int id, char *ligne);
int COMMANDE_PILE(void);
int DROP(void);
int DROP2(void);
int SWAP(void);
int DUP(void);
int DUP2(void);
int OVER(void);
int ROT(void);
int DEPTH(void);
int CLEAR(void);
int PICK(void);

int main(void)
{
    int numero_ligne = 1;
    ROOT = TOP = NULL;
		#if defined ALLOC_LOG
    		strcpy(taille_log,"");
    		taille_totale = 0;
		#endif
	while(1)
	{
		INVITE();
		 if ( ligne[0] == '.' && ligne[1] == '\0') break;
		 if ( COMMANDE_PILE() == 1 ) continue;
        //EMPILE(numero_ligne, ligne);
		 TRIM(ligne);
        numero_ligne++;
	}
  return EXIT_SUCCESS;
}

// Trim et Empile la ligne d'entree
void TRIM(char *ligne)
{
	char mot[64];
	int i, m = 0, espace_avant = 0, espace = 0, chaine = 0;
	for(i=0; i < strlen(ligne); i++)
	{
		// Saute les espaces et tabulations avant tous caracteres
		if ( (ligne[i] == ' ' || ligne[i] == '\t') && (espace_avant == 0) )
		{
			//printf("saute espace avant tout 1er caractere.\n");
			continue;
		}
		else
		{
		  //printf("autre que espace avant.\n");
		  espace_avant = 1;
	      // CHAINE ?
			// Dans chaine de caracteres ?
			if( (chaine == 1) && ( ligne[i] != '"' ))
			{
			  mot[m++] = ligne[i];
			  continue;
			}
			// Fin chaine de caractere ?
			if ( (ligne[i] == '"') && (chaine == 1) )
			{
			  mot[m++] = '"';
			  mot[m] = '\0';
			  //printf("EMPILER CHAINE:%s\n", mot);	// Empile la chaine
			  EMPILE(0, mot);
			  m = 0;
			  chaine = 0;
			  espace = 2;
			  continue;
			}
			// debut Chaine de caractere ?
			if ( (ligne[i] == '"') && (chaine == 0) )
			{
			  m = 0;
			  mot[m++] = '"';
			  chaine = 1;
			  continue;
			}
		  // AUTRE QUE CHAINE
			if ( (ligne[i] != ' ') && (ligne[i] != '\t') )
			{
			  //printf("\tcaractere mot = [%c]\n", ligne[i]);
			  mot[m++] = ligne[i];
			  mot[m] = '\0';
			  espace = 0;
			}
			else
			{
			  if ( espace == 0 )
			  {
			   // Empile le mot precedent
			   //printf("EMPILER:%s\n", mot);
			   EMPILE(0, mot);
			  }
			 espace = 1;
	         m = 0;
			}
		}
	}
	if ( m > 0 )
	{
	  //printf("RESTER A EMPILER:%s\n", mot);
		   EMPILE(0, mot);
	}
}


void EMPILE(int id, char *ligne)
{
 // TRIM spaces, tabs Todo
   //printf("LIGNE A EMPILER=[%s]\n", ligne);
   struct element *new_element;
   new_element = (struct element*)malloc(sizeof(struct element));
   new_element->id = id++;
   new_element->data = (char*)malloc(sizeof(char)*(strlen(ligne)+1));
   strcpy(new_element->data, ligne);
   if ( TOP )
	 TOP->right = new_element;		// precedent
   else
    ROOT = new_element; // tout premier element
   new_element->left = TOP;
   new_element->right = NULL; 	// suivant, n'existe pas encore
   TOP = new_element;
	#if defined ALLOC_LOG
   	   // Log alloc
   	   if (taille_totale) strcat(taille_log,"+");
   	   sprintf(buf, "%lu", sizeof(char)*(strlen(ligne)+1) );
   	   strcat(taille_log, buf);
   	   taille_totale += sizeof(char)*(strlen(ligne)+1);
	#endif
}

int COMMANDE_PILE()
{
  int continue_ou_pas = 0;
    // Commandes de Pile sans arguments
	if ( strcmp(ligne, "SWAP") == 0 ) continue_ou_pas = SWAP();
	if ( strcmp(ligne, "DROP") == 0 ) continue_ou_pas = DROP();
	if ( strcmp(ligne, "DROP2") == 0 ) continue_ou_pas = DROP2();
	if ( strcmp(ligne, "DUP") == 0 ) continue_ou_pas = DUP();
	if ( strcmp(ligne, "DUP2") == 0 ) continue_ou_pas = DUP2();
	if ( strcmp(ligne, "OVER") == 0 ) continue_ou_pas = OVER();
	if ( strcmp(ligne, "ROT") == 0 ) continue_ou_pas = ROT();
	if ( strcmp(ligne, "DEPTH") == 0 ) continue_ou_pas = DEPTH();
	if ( strcmp(ligne, "CLEAR") == 0 ) continue_ou_pas = CLEAR();
	// Commandes de Pile a 1 arguments
	if ( strcmp(ligne, "PICK") == 0 ) continue_ou_pas = PICK();

	// Truc speciaux
	if ( strcmp(ligne, "DELID") == 0 )
	{
		printf("Suppression ID #%s\n", TOP->data);
		continue_ou_pas = 1; // commande pile
	}

	if ( strcmp(ligne, "DELTDATA") == 0 )
	{
		printf("Suppression Data (from Top) [%s]\n", TOP->data);
		continue_ou_pas = 1; // commande pile
	}

	if ( strcmp(ligne, "DELRDATA") == 0 )
	{
		printf("Suppression Data (from Root) [%s]\n", TOP->data);
		continue_ou_pas = 1; // commande pile
	}

	if ( strcmp(ligne, "DELALL") == 0 )
	{
		printf("Suppression Tout Elements = [%s]\n", TOP->data);
		suppression_tous(TOP, TOP->data);
		continue_ou_pas = 1; // commande pile
	}

	if ( strcmp(ligne, "DUMP") == 0 )
	{
		print_root2top(ROOT);
		#if defined ALLOC_LOG
			printf("ALLOC = %d (%s)\n", taille_totale, taille_log);
		#endif
		continue_ou_pas = 1; // commande pile
	}

	if ( strcmp(ligne, "PMUD") == 0 )
	{
		print_top2root(TOP);
		#if defined ALLOC_LOG
			printf("ALLOC = %d (%s)\n", taille_totale, taille_log);
		#endif
		continue_ou_pas = 1;
	}
  return continue_ou_pas;
}

// SWAP
int SWAP(void)
{
  struct element *top, *precedent, *precedent_precedent;
	  //printf("Swapping [%s] & [%s]\n", TOP->data, TOP->left->data);
	  //printf("%s\tTOP=%p [TOP->left=%p TOP->right=%p]\n", TOP->data, TOP, TOP->left, TOP->right);
	  //printf("%s\tTOP->left=%p [TOP->left->left=%p TOP->left->right=%p]\n", TOP->left->data, TOP->left, (TOP->left)->left, (TOP->left)->right);
		// au minimum 2 niveaux ?
		if ( TOP->left == NULL )
		{
		 printf("UNDERFLOW!\n");
			exit(-1);
		}
		// Juste 2 niveaux ?
		if ( TOP->left->left == NULL )
		{
		  //printf("2 NIVEAUX !\n");
		  top = TOP;
		  precedent = ROOT;
		  top->left = NULL;
		  top->right = precedent;
		  precedent->left = top;
		  precedent->right = NULL;
		  TOP = precedent;
		  ROOT = top;
		}
		else
		{
		 top = TOP;
		 precedent = TOP->left;
		 precedent_precedent = TOP->left->left;
		 // dernier devient avant-dernier
         precedent_precedent->right = top;
         top->left = precedent_precedent;
         // avant-dernier devient dernier
         top->right = precedent;
         precedent->left = top;
         precedent->right = NULL;
         TOP = precedent;
		}
	return 1;
}
// DROP
int DROP(void)
{
   struct element *temp;
	if ( TOP == NULL )
	{
	  printf("UNDERFLOW!\n");
	   exit(-1);
	}
	//printf("Dropping [%s]\n", TOP->data);
	// 1 seul niveau ?
	if ( TOP->left == NULL )
	{
	  //printf("1 seul niveau\n");
			#if defined ALLOC_LOG
		    	// Log Taille
				strcat(taille_log, "-");
				sprintf(buf, "%lu", sizeof(char)*(strlen(TOP->data)+1) );
				strcat(taille_log, buf);
				strcat(taille_log, " raz ");
				taille_totale = taille_totale - sizeof(char)*(strlen(TOP->data)+1);
			#endif
		free(TOP->data);
		free(TOP);
		ROOT = TOP = NULL;
	}
	else
	{
		#if defined ALLOC_LOG
	    	// Log Taille
			strcat(taille_log, "-");
			sprintf(buf, "%lu", sizeof(char)*(strlen(TOP->data)+1) );
			strcat(taille_log, buf);
			taille_totale = taille_totale - sizeof(char)*(strlen(TOP->data)+1);
		#endif
		temp = TOP->left; // Valgrind
		free(TOP->data);
		free(TOP);
		TOP = temp;
		TOP->right = NULL;
	}
	return 1;
}
// DROP2
int DROP2(void)
{
   DROP();
   return DROP();
}
// DUP
int DUP(void)
{
	if ( TOP == NULL )
	{
	  printf("UNDERFLOW!\n");
	   exit(-1);
	}
	EMPILE(0, TOP->data);
 return 1;
}
// DUP2
int DUP2(void)
{
  DUP();
  return DUP();
}
// ROT
int ROT(void)
{
   printf("ROT is TODO !\n");
 return 1;
}
// OVER
int OVER(void)
{
	if ( TOP == NULL && TOP->left == NULL )
	{
	  printf("UNDERFLOW!\n");
	   exit(-1);
	}
   EMPILE(0, TOP->left->data);
  return 1;
}
// DEPTH
int DEPTH(void)
{
  int depth=0;
  char buf[7];
	struct element *current;
	printf("Iteration de la pile pour calculer sa profondeur...\n");
	current = ROOT;
	while( current )
	{
		depth++;
		current = current->right;
	}
	snprintf(buf, 7, "%d", depth);
   EMPILE(0, buf);
 return 1;
}
// CLEAR
int CLEAR(void)
{
 printf("TODO - CLEAR !\n");
  return 1;
}
// PICK
int PICK(void)
{
  int i, level;
  struct element *current;
	if ( TOP == NULL )
	{
	  printf("UNDERFLOW!\n");
	   exit(-1);
	}
   level = atoi(TOP->data);
   DROP();
   if ( level > 0 )
   {
		current = TOP;
		i = 0;
		while( current )
		{
			//printf("#%d, DATA=[%s]\n", current->id, current->data);
			current = current->left;
			i++;
			if ( i == (level-1) )
			{
				printf("PICK Element = [%s]\n", current->data);
				EMPILE(0, current->data);
				break;
			}
		}
   }
  return 1;
}



// Invite Pile
void INVITE(void)
{
	char ch;
    int position_ligne=0;
	printf(">");
	while (1 )
	{
		ch = getchar();
		if  ( ch == 10 ) break;
		ligne[position_ligne++] = ch;
		ligne[position_ligne] = '\0';
	}
}


void suppression_tous(struct element *start, char *data)
{
	struct element *current;
	printf("*** SUPPRESSION TOUS A PARTIR DE TOP ***\n");
	current = start;
	while( current )
	{
		if ( strcmp(data, current->data) == 0 )
		 printf("-> A SUPPRIMER: #%d, DATA=[%s]\n", current->id, current->data);
		current = current->left;
	}
}


int print_top2root(struct element *start)
{
	struct element *current;
	printf("*** TOP to ROOT ***\n");
	current = start;
	while( current )
	{
		printf("#%d, DATA=[%s]\n", current->id, current->data);
		current = current->left;
	}
	return 0;
}

int print_root2top(struct element *start)
{
	struct element *current;
	printf("*** ROOT to TOP ***\n");
	current = start;
	while( current )
	{
		printf("#%d, DATA=[%s]\n", current->id, current->data);
		current = current->right;
	}
	return 0;
}

int print_id(struct element *start, int id)
{
	struct element *current;
	//printf("*** ROOT to TOP ***\n");
	current = start;
	while( current )
	{
		if ( id == current->id )
		 printf("#%d, DATA=[%s]\n", current->id, current->data);
		current = current->right;
	}
	return 0;
}



